<?php
// +----------------------------------------------------------------------
// | ImportExcel服务层
// +----------------------------------------------------------------------
// | 主要服务于Excel文件导入处理
// +----------------------------------------------------------------------
// | 错误编码头 217xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\exception\LogicException;
use common\model\EntityModel;
use common\model\PlanModel;
use common\model\ProjectModel;
use common\model\StepModel;
use PhpOffice\PhpSpreadsheet\IOFactory;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\module\Module;

class ImportExcelService
{
    use SingletonTrait;

    /**
     * 待处理表格数据
     * @var array
     */
    protected $sheetData = [];

    /**
     * 保存列号
     * @var array
     */
    protected $columnNumberList = [];

    /**
     * 判断是否有表头
     * @var bool
     */
    protected $haveHeaderStatus = false;

    /**
     * 图片信息
     * @var array
     */
    protected $imageData = [];

    /**
     * @var array
     */
    protected $headerMap = [];

    /**
     * @var array
     */
    protected $currentModuleLinkRelation = [];

    /**
     * @var \common\service\mediaService
     */
    protected $mediaService;

    /**
     * 实例化Spreadsheet对象
     * @param $filePath
     * @return \PhpOffice\PhpSpreadsheet\Spreadsheet
     */
    protected function initSpreadsheet($filePath)
    {
        try {
            return IOFactory::load($filePath);
        } catch (\Exception $e) {
            throw_strack_exception($e->getMessage(), ErrorCode::EXCEL_FILE_DOES_NOT_EXIST);
        }
    }

    /**
     * 获取图像数据
     * @param \PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet
     * @param $tempExcelRelativePath
     * @return array
     * @throws \Exception
     */
    protected function getImages(\PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet, $tempExcelRelativePath)
    {
        $tempDirectoryName = 'excel_images/';
        $tempExcelPath = $_SERVER['DOCUMENT_ROOT'] . __ROOT__ . $tempExcelRelativePath;

        // 获取当前excel图片数据
        $imageDrawingData = [];
        foreach ($spreadsheet->getActiveSheet()->getDrawingCollection() as $drawing) {
            $zipReader = fopen($drawing->getPath(), 'r');
            $imageContents = '';
            while (!feof($zipReader)) {
                $imageContents .= fread($zipReader, 1024);
            }
            fclose($zipReader);

            //图片左上角所在单元格
            $coordinates = $drawing->getCoordinates();

            //保存图片目录
            $cellFileName = $tempDirectoryName . 'excel_image_temp' . string_random(8) . '.jpg';

            array_push($imageDrawingData, [
                "coordinates" => $coordinates,
                "name" => $cellFileName,
                "content" => $imageContents
            ]);
        }


        // 如果存在图片把它保存到临时目录
        $imageData = [];
        $imagesExitCoordinates = [];

        if (!empty($imageDrawingData)) {

            // 创建图片临时存放目录
            create_directory($tempExcelPath . $tempDirectoryName);

            // 遍历图片数据保存到临时目录
            foreach ($imageDrawingData as $value) {
                if (in_array($value["coordinates"], $imagesExitCoordinates)) {
                    // 同一个单元格不能存在多张图片，返回错误单元格,让用户手动修改
                    throw_strack_exception($value["coordinates"] . "_" . 'Excel image overlap!', ErrorCode::ONE_CELL_CANNOT_HAVE_MORE_THAN_ONE_PICTURE);
                } else {
                    array_push($imagesExitCoordinates, $value["coordinates"]);

                    // 保存到临时目录
                    $tempPath = $tempExcelPath . '' . $value["name"];
                    file_put_contents($tempPath, $value["content"]);

                    $imageData[$value["coordinates"]] = $tempExcelRelativePath . '' . $value["name"];
                }
            }
        }

        return $this->imageData = $imageData;
    }

    /**
     * 传入文件得到excel数据
     * @param $filePath
     * @param $tempExcelRelativePath
     * @throws \Exception
     */
    public function file($filePath, $tempExcelRelativePath)
    {
        // 初始化 Spreadsheet 对象
        $spreadsheet = $this->initSpreadsheet($filePath);
        $sheetData = $spreadsheet->getActiveSheet()->toArray('', true, true, true);
        if (empty($sheetData)) {
            throw_strack_exception('Input excel data require', ErrorCode::INPUT_EXCEL_DATA_REQUIRE);
        }
        $this->columnNumberList = array_keys($sheetData[1]);
        // 获取图片数据
        $imagesData = $this->getImages($spreadsheet, $tempExcelRelativePath);
        foreach ($imagesData as $key => $value) {
            preg_match_all('/([0-9]+|[a-zA-Z]+)/', $key, $data);
            list($row, $column) = $data[0];
            $sheetData[$column][$row] = empty($sheetData[$column][$row]) ? $value : throw_strack_exception($row . $column . "_" . 'The picture cell cannot have data! ', ErrorCode::PICTURE_CELL_CANNOT_HAVE_DATA);
        }
        $this->sheetData = $sheetData;
    }


    /**
     * 粘贴数据
     * @param $data
     */
    public function paste($data)
    {
        $this->sheetData = $data;
    }


    /**
     * 获取待处理数据
     * @return array
     */
    public function getData()
    {
        return $this->sheetData;
    }

    /**
     * 获取第一行索引列表
     * @return array
     */
    protected function getFirstColumnIndex()
    {
        // 无表头取列标号作为表头例如 [A1,A2,A3]
        if (!empty($this->columnNumberList)) {
            // 存在序号列表直接返回
            return $this->columnNumberList;
        } else {
            // 不存在取第一行数据，组装返回
            $maxColumnNumber = count(reset($this->sheetData));
            $countColumnNumber = 0;
            for ($k = 'A'; $k <= 'ZZ'; $k++) {
                $countColumnNumber++;
                if ($countColumnNumber <= $maxColumnNumber) {
                    array_push($this->columnNumberList, $k);
                } else {
                    break;
                }
            }
            return $this->columnNumberList;
        }
    }

    /**
     * 设置是否有表头
     * @param $haveHeader
     */
    public function setHeaderStatus($haveHeader)
    {
        $this->haveHeaderStatus = $haveHeader;
    }

    /**
     * 获得表头
     * @return array
     */
    public function getHeader()
    {
        if (!empty($this->sheetData)) {

            $columnNumberList = $this->getFirstColumnIndex();
            $firstRowData = reset($this->sheetData);
            if (!in_array("id", $firstRowData)) {
                //最后一个key
                end($firstRowData);
                $last = key($firstRowData);
                $last++;
                $firstRowData[$last] = "id";
            }

            if ($this->haveHeaderStatus) {
                // 有表头取第一行字段
                $firstColumnKeyList = [];
                //处理掉特殊字符串
                $replace = ["(", ")", "*", ':', '/', '\\', '?', '[', ']', '.', ' '];
                $replaceList = array_fill_keys($replace, "");
                foreach ($firstRowData as $key => $value) {
                    $value = replace_string_to_specified_value($value, $replaceList);
                    if (!empty($value)) {
                        array_push($firstColumnKeyList, (string)$value);
                    } else {
                        // 当前单元格值为空，用列号补位
                        array_push($firstColumnKeyList, preg_replace('/[0-9]/', '', $key));
                    }
                }
                $header = $firstColumnKeyList;
            } else {
                $header = $columnNumberList;
            }
            if (!in_array("id", $header)) {
                array_push($header, "id");
            }
            $this->headerMap = array_combine(array_keys($firstRowData), $header);
            return $header;
        } else {
            // 不存在数据，报错
            throw_strack_exception('Excel data cannot be empty!', ErrorCode::EXCEL_DATA_CANNOT_BE_EMPTY);
        }
    }

    /**
     * 获取存在图片的单元格
     * @return array
     */
    public function getHaveImageCell()
    {
        if (!empty($this->imageData)) {
            $haveImageHeader = [];
            foreach ($this->imageData as $key => $value) {
                $cellIndex = preg_replace('/[0-9]/', '', $key);
                if (!in_array($cellIndex, $haveImageHeader)) {
                    array_push($haveImageHeader, $this->headerMap[$cellIndex]);
                }
            }
            return $haveImageHeader;
        } else {
            return [];
        }
    }

    /**
     * 获取表格正文数据
     * @return array
     */
    public function getBody()
    {
        if (!empty($this->sheetData)) {
            //body 数据键值为表头
            $data = [];
            $id = 0;
            foreach ($this->sheetData as $key => $value) {
                $rowData = [];
                foreach ($value as $k => $v) {
                    $rowData[$this->headerMap[$k]] = $v;
                }
                if (!array_key_exists("id", $rowData)) {
                    $rowData["id"] = $id++;
                }
                array_push($data, $rowData);
            }
            $this->sheetData = $data;
            if ($this->haveHeaderStatus) {
                // 有表头除去第一行字段
                $bodyData = $this->sheetData;
                array_shift($bodyData);
                return $bodyData;
            } else {
                // 无表头返回所有数据
                return $this->sheetData;
            }
        } else {
            // 不存在数据，报错
            throw_strack_exception('Excel data cannot be empty!', ErrorCode::EXCEL_DATA_CANNOT_BE_EMPTY);
        }
    }


    /**
     * 填入Excel导入数据
     * @param $data
     * @param $fields
     * @param $values
     * @return mixed
     * @throws \Exception
     */
    protected function fillImportExcelItemData(&$data, $fields, $values)
    {
        foreach ($fields as $field => $fieldMap) {

            $fieldValue = '';
            if (!array_key_exists($fieldMap, $values)) {
                throw_strack_exception("The {$fieldMap} field must have data!", ErrorCode::EXCEL_CELL_MUST_HAVE_DATA);
            }

            if (array_key_exists($field, $this->currentModuleLinkRelation)
                && $this->currentModuleLinkRelation[$field]['type'] === 'horizontal') {
                // 处理水平关联字段
                switch ($this->currentModuleLinkRelation[$field]['module_code']) {
                    case 'media':
                        $mediaUploadData = $this->mediaService->uploadMedia($_SERVER['DOCUMENT_ROOT'] . __ROOT__ . $values[$fieldMap]);
                        if (!empty($mediaUploadData)) {
                            $addMediaData = $this->mediaService->saveMediaData($mediaUploadData);
                            $fieldValue = $addMediaData['id'];
                        }
                        break;
                    default:
                        $fieldValue = $values[$fieldMap];
                        break;
                }
            } else {
                $fieldValue = $values[$fieldMap];
            }


            $data[$field] = $fieldValue;
        }

        return $data;
    }

    /**
     * 格式化实体Excel数据
     * @param $itemData
     * @param $exitModule
     * @param $currentModuleData
     * @param $entityModuleConfig
     * @param $param
     * @param $fieldMap
     * @param $item
     * @param bool $preExit
     * @param string $pointModuleCode
     * @return mixed
     * @throws \Exception
     */
    protected function formatEntityExcelItemData(&$itemData, $exitModule, $currentModuleData, $entityModuleConfig, $param, $fieldMap, $item, $preExit = false, $pointModuleCode = '')
    {

        if (empty($fieldMap[$entityModuleConfig['belong_module']]) && ($pointModuleCode !== $currentModuleData['code']) && $preExit) {
            throw_strack_exception('Missing intermediate data!', ErrorCode::MISSING_INTERMEDIATE_DATA);
        }

        $pointModuleCode = $entityModuleConfig['belong_module'];

        if (!empty($fieldMap[$entityModuleConfig['belong_module']])) {
            $itemData[$entityModuleConfig['belong_module']] = [
                "data" => [],
                "after" => []
            ];
            $this->fillImportExcelItemData($itemData[$entityModuleConfig['belong_module']]['data'], $fieldMap[$entityModuleConfig['belong_module']], $item);

            if (!empty($entityModuleConfig['child'])) {
                $this->formatEntityExcelItemData($itemData[$entityModuleConfig['belong_module']]['after'], $exitModule, $currentModuleData, $entityModuleConfig['child'], $param, $fieldMap, $item, true, $pointModuleCode);
            }

        } else {
            if (!empty($entityModuleConfig['child'])) {
                $this->formatEntityExcelItemData($itemData, $exitModule, $currentModuleData, $entityModuleConfig['child'], $param, $fieldMap, $item, false, $pointModuleCode);
            }
        }

        if (
            empty($entityModuleConfig['child'])
            && $entityModuleConfig['belong_module'] === $currentModuleData['code']) {
            foreach ($exitModule as $exitModuleCode) {
                $itemData[$pointModuleCode]['after'][$exitModuleCode] = [
                    "data" => []
                ];
                $this->fillImportExcelItemData($itemData[$pointModuleCode]['after'][$exitModuleCode]['data'], $fieldMap[$exitModuleCode], $item);
            }
        }

        return $itemData;
    }

    /**
     * 处理Excel主表从属数据
     * @param $item
     * @param $fieldMap
     * @param $exitBelongToModule
     * @param $currentModuleData
     */
    protected function generateImportBelongToData(&$item, &$fieldMap, $exitBelongToModule, $currentModuleData)
    {
        foreach ($exitBelongToModule as $moduleCode) {
            $modelObjectClass = get_module_model_name(Module::$moduleDictData['module_index_by_code'][$moduleCode]);
            $newModelObject = new $modelObjectClass();

            $addData = [];
            foreach ($fieldMap[$moduleCode] as $filed => $map) {
                $addData[$filed] = $item[$map];
            }

            // belong_to 数据必须要存在不然抛出异常
            $belongToId = $newModelObject->where($addData)->getField('id');
            if ($belongToId > 0) {
                $item[$this->currentModuleLinkRelation[$moduleCode]['link_id']] = $belongToId;
                $fieldMap[$currentModuleData['code']][$this->currentModuleLinkRelation[$moduleCode]['link_id']] = $this->currentModuleLinkRelation[$moduleCode]['link_id'];
            } else {
                throw_strack_exception("Dependent {$moduleCode} data does not exist", ErrorCode::DEPENDENT_DATA_DOES_NOT_EXIST);
            }
        }
    }

    /**
     * 格式化Excel导入数据
     * @param $currentModuleLinkRelation
     * @param $param
     * @param $currentModuleData
     * @return array
     * @throws \Exception
     */
    protected function formatImportData($currentModuleLinkRelation, $param, $currentModuleData)
    {
        $fieldMap = [];
        $exitModule = [];
        $exitBelongToModule = [];
        $exitMasterData = false;

        $this->mediaService = MediaService::getInstance();

        foreach ($param['header_mapping'] as $headerItem) {
            $fieldArray = explode('.', $headerItem['field']);
            if (array_key_exists($fieldArray[0], $fieldMap)) {
                $fieldMap[$fieldArray[0]][$fieldArray[1]] = $headerItem['column_name'];
            } else {
                $fieldMap[$fieldArray[0]] = [$fieldArray[1] => $headerItem['column_name']];
            }

            if (!in_array($fieldArray[0], $exitModule)
                && $fieldArray[0] !== $currentModuleData['code']
                && Module::$moduleDictData['module_index_by_code'][$fieldArray[0]]['type'] !== 'entity'
            ) {

                if ($this->currentModuleLinkRelation[$fieldArray[0]]['relation_type'] === 'belong_to') {
                    $exitBelongToModule[] = $fieldArray[0];
                } else {
                    $exitModule[] = $fieldArray[0];
                }
            }

            if ($fieldArray[0] === $currentModuleData['code']) {
                $exitMasterData = true;
            }
        }

        // 判断是否包含主表数据
        if (!$exitMasterData) {
            throw_strack_exception('Must contain master table data!', ErrorCode::MUST_CONTAIN_MASTER_TABLE_DATA);
        }

        $formatData = [];

        foreach ($param['body'] as $item) {
            $itemData = [];

            // 先处理belong_to
            $this->generateImportBelongToData($item, $fieldMap, $exitBelongToModule, $currentModuleData);

            if ($currentModuleData['type'] !== "entity") {
                // 普通模块新增
                $itemData = [];
                $itemData[$currentModuleData['code']] = [
                    "data" => [],
                    "after" => []
                ];

                $this->fillImportExcelItemData($itemData[$currentModuleData['code']]['data'], $fieldMap[$currentModuleData['code']], $item);

                foreach ($exitModule as $exitModuleCode) {
                    $itemData[$currentModuleData['code']]['after'][$exitModuleCode] = [
                        "data" => []
                    ];
                    $this->fillImportExcelItemData($itemData[$currentModuleData['code']]['after'][$exitModuleCode]['data'], $fieldMap[$exitModuleCode], $item);
                }
            } else {
                // 处理实体模块新增数据
                $this->formatEntityExcelItemData($itemData, $exitModule, $currentModuleData, $currentModuleLinkRelation[$currentModuleData['code']], $param, $fieldMap, $item, false);
            }

            $formatData[] = [
                'data' => $itemData,
                "module_code" => $currentModuleData['code']
            ];
        }

        return $formatData;
    }

    /**
     *      * 导入Excel
     * @param $param
     * @param $currentModuleData
     * @return array
     * @throws \Exception
     */
    public function importExcelData($param, $currentModuleData)
    {
        // 1. 获取当前模块关联关系
        $class = get_module_model_name($currentModuleData);

        $objectModel = new $class();
        $objectModel->setCurrentModuleCode($param['module_code']);
        $currentModuleLinkRelation = $objectModel->parserFilterModuleRelation(true);
        $this->currentModuleLinkRelation = $currentModuleLinkRelation;

        $formatData = $this->formatImportData($currentModuleLinkRelation, $param, $currentModuleData);

        // shot

        $commonService = CommonService::instance(get_module_table_name($currentModuleData));
        $resData = [];

        foreach ($formatData as $formatItem) {
            $addData = $commonService->commonCreate($formatItem, $currentModuleData, false);
            $resData = array_merge($resData, $addData);
        }

        return $resData;
    }

    /**
     * 检查字段是否满足属性验证
     * @param $moduleCode
     * @param $field
     * @param $value
     * @param $current
     * @return bool
     */
    public function getModuleFieldAttributeValueCheck($moduleCode, $field, $value, &$current)
    {
        if (empty($value)) {
            return true;
        }

        if (!isset($current[$moduleCode])) {
            $fieldService = FieldService::getInstance();
            $attributeRuleMapByCode = $fieldService->getEntityAttributeCustomFieldsMapping(module_by_code($moduleCode)['id'], 'code');
            $attributeValueList = AttributeValueService::getInstance()->getAttributeValueList(array_column($attributeRuleMapByCode, 'attribute_id'));
            $attributeFieldsMap = array_column($attributeRuleMapByCode, 'attribute_id', 'field');
            $attributeValueList = array_group_by($attributeValueList, 'attribute_id');
            $attributeValueList = array_map(function ($i) {
                return array_column($i, 'value');
            }, $attributeValueList);
            $current[$moduleCode]['attribute_field_relation'] = $attributeFieldsMap;
            $current[$moduleCode]['field_attribute_list'] = $attributeValueList;
            foreach ($current[$moduleCode]['attribute_field_relation'] as $currentField => $currentAttributeId) {
                $current[$moduleCode]['field_attribute_value_list'][$currentField] = $current[$moduleCode]['field_attribute_list'][$currentAttributeId] ?? [];
            }
        }
        $attributeId = $current[$moduleCode]['attribute_field_relation'][$field] ?? 0;
        if ($attributeId == 0) {
            return true;
        }

        return in_array($value, $current[$moduleCode]['field_attribute_list'][$attributeId]);
    }

    /**
     * 字段规则检查
     * @param $param
     * @param $projectId
     * @param $mainCode
     * @return array
     * @throws Exception
     */
    public function checkExcelFileData($param, $projectId, $mainCode)
    {
        $project = model(ProjectModel::class)->find($param['project_id']);
        if (empty($project)) {
            throw new LogicException("project not found", ErrorCode::PROJECT_NOT_FOUND);
        }
        $templateService = TemplateService::getInstance();
        $moduleFieldConfig = $templateService->getProjectTemplateModuleFieldConfig($project['project_template_id']);


        $res = [];
        // unique check
        $uniqueFields = [];
        if (!empty($moduleFieldConfig[$mainCode]['fields'])) {
            foreach ($moduleFieldConfig[$mainCode]['fields'] as $field) {
                if (!empty($field['unique_check']) && $field['unique_check'] === 'yes') {
                    $uniqueFields[] = $field['code'];
                }
            }
        }


        $needUniqueCheck = !empty($uniqueFields);

        $headerMap = array_column($param['header_mapping'], "field", 'column_name');
        $headerMapReverse = array_flip($headerMap);

        // 获取剧集
        $moduleData = Module::$moduleDictData['module_index_by_code']['episode'];
        $entityModel = model(EntityModel::class);
        $where = [
            "module_id" => $moduleData['id'],
            'project_id' => $param['project_id']
        ];
        $rows = $entityModel->field('code,name')
            ->where($where)
            ->order("LENGTH(code) asc,code asc")
            ->select();
        $entityMap = array_column($rows, null, 'code');

        $fieldAttributeRelationData = [];
        foreach ($param['body'] as $row) {
            $isMatchRule = true;


            if ($needUniqueCheck) {
                $uniqueMapData = [];
                foreach ($uniqueFields as $field) {
                    if ($field === 'project_id') {
                        $uniqueMapData['project_id'] = $projectId;
                        continue;
                    }
                    if ($field === 'module_id') {
                        $uniqueMapData['module_id'] = Module::$moduleDictData['module_index_by_code'][$mainCode]['id'];
                        continue;
                    }
                    if (empty($headerMapReverse[$mainCode . '.' . $field]) || empty($row[$headerMapReverse[$mainCode . '.' . $field]])) {
                        $row[$mainCode . '.' . $field] = $mainCode . '.' . $field . ' 必传';
                        break;
                    }
                    $uniqueMapData[$field] = $row[$headerMapReverse[$mainCode . '.' . $field]];
                }
                $existCheck = model(EntityModel::class)->alias($mainCode)->where(parse_filter_to_tp_where($uniqueMapData, $mainCode))->field('id')->find();
                if (!empty($existCheck)) {
                    $isMatchRule = false;
                    $row['unique_check'] = false;
                }
            }

            foreach ($row as $fieldCName => $fieldValue) {


                if (!array_key_exists($fieldCName, $headerMap)) {
                    continue;
                }
                list($moduleCode, $field) = explode('.', $headerMap[$fieldCName]);

                // 检查是否为属性字段
                if (!$this->getModuleFieldAttributeValueCheck($moduleCode, $field, $fieldValue, $fieldAttributeRelationData)) {
                    $currentAttributeValueAllowed = $fieldAttributeRelationData[$moduleCode]['field_attribute_value_list'][$field] ?? [];
                    $row[$fieldCName] = $row[$fieldCName] . ' (错误: 允许值(' . implode(',', $currentAttributeValueAllowed) . '))';
                }

                if ($field == 'initial_episode_code') {
                    // 检测导入数据中的剧集是否存在
                    if (!array_key_exists($fieldValue, $entityMap)) {
                        $isMatchRule = false;
                        $row[$fieldCName] = $row[$fieldCName] . ' (错误: 该剧集不存在)';
                        continue;
                    }
                }
                $rule = $moduleFieldConfig[$moduleCode]['fields'][$field] ?? null;
                if (empty($rule)) {
                    continue;
                }
                if ($needUniqueCheck && $mainCode === $moduleCode && in_array($field, $uniqueFields) && (is_null($fieldValue) || $fieldValue === '')) {
                    // 当前字段包含在唯一性验证的字段里,就需要检查是不是传值
                    // null或者'' 被视为没传值
                    $isMatchRule = false;
                    $row[$fieldCName] = $row[$fieldCName] . ' (错误: 必传字段不能为空)';
                    continue;
                }
                if (!empty($rule['required']) && $rule['required'] === 'yes' && (is_null($fieldValue) || $fieldValue === '')) {
                    // 必传验证
                    // null或者'' 被视为没传值
                    $isMatchRule = false;
                    $row[$fieldCName] = $row[$fieldCName] . ' (错误: 必传字段不能为空)';
                    continue;
                }

                if (!empty($rule['default_prefix'])) {
                    if (false === str_starts_with($fieldValue, $rule['default_prefix'])) {
                        $isMatchRule = false;
                        $row[$fieldCName] = $row[$fieldCName] . ' (错误: 应以' . $rule['default_prefix'] . '为前缀)';
                        continue;
                    }
                    // 移除前缀
                    $fieldValue = str_replace_once($rule['default_prefix'], '', $fieldValue);
                }

//                if ($rule['number_pad_length'] > 0) {
//                    if (mb_strlen(trim($fieldValue)) < $rule['number_pad_length']) {
//                        $isMatchRule = false;
//                        $row[$fieldCName] = $row[$fieldCName] . ' (错误: 数字部分长度应该大于等于' . $rule['number_pad_length'] . ')';
//                        continue;
//                    }
//                }

                if (!empty($rule['number_pad_length_min'])) {
                    if (mb_strlen(trim($fieldValue)) < $rule['number_pad_length_min']) {
                        $isMatchRule = false;
                        $row[$fieldCName] = $row[$fieldCName] . ' (错误: 数字部分长度应该大于等于' . $rule['number_pad_length_min'] . ')';
                        continue;
                    }
                }

                if (!empty($rule['number_pad_length_max']) && $rule['number_pad_length_max'] > 0) {
                    if (mb_strlen(trim($fieldValue)) > $rule['number_pad_length_max']) {
                        $isMatchRule = false;
                        $row[$fieldCName] = $row[$fieldCName] . ' (错误: 数字部分长度应该小于等于' . $rule['number_pad_length_max'] . ')';
                        continue;
                    }
                }

                if (!empty($rule['validation_rules'])) {
                    $validateRules = validate_input_type($rule['validation_rules'], $fieldValue);
                    if ($validateRules['code'] == 0) {
                        $validationRulesString = implode(',', $validateRules['preg_match_rules']);
                        $isMatchRule = false;
                        $row[$fieldCName] = $row[$fieldCName] . ' (错误: 只允许' . $validationRulesString . ')';
                        continue;
                    }
                }
            }
            if ($isMatchRule) {
                $res['success'][] = $row;
            } else {
                $res['failure'][] = $row;
            }
        }

        return $res;
    }

    /**
     * 检查表单数据
     * @param $param
     * @param $projectId
     * @param $moduleCode
     * @return array
     * @throws Exception
     */
    public function checkFormData($param, $projectId, $moduleCode)
    {
        $project = ProjectService::getInstance()->getProject($projectId, 'id,project_template_id', true);
        $moduleFieldConfig = TemplateService::getInstance()->getProjectTemplateModuleFieldConfig($project['project_template_id']);
        $res = [];
        // unique check
        $uniqueFields = [];
        if (!empty($moduleFieldConfig[$moduleCode]['fields'])) {
            foreach ($moduleFieldConfig[$moduleCode]['fields'] as $field) {
                if (!empty($field['unique_check']) && $field['unique_check'] === 'yes') {
                    $uniqueFields[] = $field['code'];
                }
            }
        }

        $initialEpisodeCode = [];
        foreach ($param['rows'] as $row) {
            if (!empty($row['initial_episode_code'])) {
                $initialEpisodeCode[$row['initial_episode_code']] = $row['initial_episode_code'];
            }
        }

        //判断集是否存在 ,集存在的不能导入
        $episodeList = [];
        if ($moduleCode === 'storyboard' && !empty($initialEpisodeCode)) {
            $episodeList = model(EntityModel::class)
                ->where(['code' => ['IN', $initialEpisodeCode], 'project_id' => $projectId, 'module_id' => module_by_code('episode')['id']])->field('code')->select();
            $episodeList = array_column($episodeList, 'code');
        }

        $fieldAttributeRelationData = [];
        $needUniqueCheck = !empty($uniqueFields);
        foreach ($param['rows'] as $row) {
            $isMatchRule = true;
            foreach ($row as $fieldName => $fieldValue) {
                // 属性字段检查
                if (!$this->getModuleFieldAttributeValueCheck($moduleCode, $fieldName, $fieldValue, $fieldAttributeRelationData)) {
                    $currentAttributeValueAllowed = $fieldAttributeRelationData[$moduleCode]['field_attribute_value_list'][$fieldName] ?? [];
                    $isMatchRule = false;
                    $row['error_message'][] = $fieldName . ' (错误: 允许值(' . implode(',', $currentAttributeValueAllowed) . '))';
                    continue;
                }

                $rule = $moduleFieldConfig[$moduleCode]['fields'][$fieldName] ?? null;
                if (empty($rule)) {
                    continue;
                }
                if ($needUniqueCheck && in_array($fieldName, $uniqueFields) && (is_null($fieldValue) || $fieldValue === '')) {
                    $isMatchRule = false;
                    $row['error_message'][] = $fieldName . ' (错误: 必传字段不能为空)';
                    continue;
                }

                if (!empty($rule['required']) && $rule['required'] === 'yes' && (is_null($fieldValue) || $fieldValue === '')) {
                    // 必传验证
                    // null或者'' 被视为没传值
                    $isMatchRule = false;
                    $row['error_message'][] = $fieldName . ' (错误: 必传字段不能为空)';
                    continue;
                }

                if ($moduleCode === 'storyboard' && $fieldName === 'initial_episode_code' && !in_array($fieldValue, $episodeList)) {
                    // 必传验证
                    // null或者'' 被视为没传值
                    $isMatchRule = false;
                    $row['error_message'][] = '上传失败: 剧集不存在';
                    continue;
                }

                if (!empty($rule['default_prefix'])) {
                    if (false === str_starts_with($fieldValue, $rule['default_prefix'])) {
                        $isMatchRule = false;
                        $row['error_message'][] = $fieldName . ' (错误: 应以' . $rule['default_prefix'] . '为前缀)';
                        continue;
                    }
                    // 移除前缀
                    $fieldValue = str_replace_once($rule['default_prefix'], '', $fieldValue);
                }
//                if ($rule['number_pad_length'] > 0) {
//                    if (mb_strlen(trim($fieldValue)) < $rule['number_pad_length']) {
//                        $isMatchRule = false;
//                        $row['error_message'][] = $fieldName . ' (错误: 数字部分长度应该大于等于' . $rule['number_pad_length'] . ')';
//                        continue;
//                    }
//                }

                if (!empty($rule['number_pad_length_min'])) {
                    if (mb_strlen(trim($fieldValue)) < $rule['number_pad_length_min']) {
                        $isMatchRule = false;
                        $row['error_message'][] = $fieldName . ' (错误: 长度应该大于等于' . $rule['number_pad_length_min'] . ')';
                        continue;
                    }
                }

                if (!empty($rule['number_pad_length_max']) && $rule['number_pad_length_max'] > 0) {
                    if (mb_strlen(trim($fieldValue)) > $rule['number_pad_length_max']) {
                        $isMatchRule = false;
                        $row['error_message'][] = $fieldName . ' (错误: 长度应该小于等于' . $rule['number_pad_length_max'] . ')';
                        continue;
                    }
                }
            }
            if ($needUniqueCheck) {
                $uniqueMapData = [];
                foreach ($uniqueFields as $field) {
                    if ($field === 'project_id') {
                        $uniqueMapData['project_id'] = $projectId;
                        continue;
                    }
                    if ($field === 'module_id') {
                        $uniqueMapData['module_id'] = Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'];
                        continue;
                    }
                    if ($field === 'code' && isset($row['name'])) {
                        $uniqueMapData['code'] = generate_code($row['name'], '');
                        continue;
                    }
                    if (empty($row[$field])) {
                        $row['error_message'][] = $field . ' 必传';
                        break;
                    }
                    $uniqueMapData[$field] = $row[$field];
                }
                $existCheck = model(EntityModel::class)->alias($moduleCode)->where(parse_filter_to_tp_where($uniqueMapData, $moduleCode))->field('id')->find();
                if (!empty($existCheck)) {
                    $isMatchRule = false;
                    $row['error_message'][] = implode(',', $uniqueFields) . '唯一性验证失败';
                }
            }
            if ($isMatchRule) {
                $res['success'][] = $row;
            } else {
                $res['failure'][] = $row;
            }
        }
        return $res;
    }

    /**
     * 检查任务更新数据
     * @param $dataList
     * @param $projectId
     * @return array
     * @throws Exception
     */
    public function checkUpdateTaskDateData($dataList, $projectId)
    {
        $res = [];
        $stepNameList = [];
        $entityFilter = [];

        $uk = "concat(entity.initial_episode_code,'#',entity.showings_number,'#',entity.name,'#',step.name) as uk";

        foreach ($dataList as &$data) {
            $stepNameList[$data['step_name']] = $data['step_name'];
            $entityFilter[] = ['entity.initial_episode_code' => $data['initial_episode_code'], 'entity.showings_number' => $data['showings_number'], 'entity.name' => $data['name']];
            $data['uk'] = $data['initial_episode_code'] . '#' . $data['showings_number'] . '#' . $data['name'] . '#' . $data['step_name'];
            if (isset($data['plan_start_time'])) {
                $data['plan_start_time_unix'] = strtotime($data['plan_start_time']);
            }
            if (isset($data['plan_end_time'])) {
                $data['plan_end_time_unix'] = strtotime($data['plan_end_time']);
            }
        }
        $allowModuleIdList = [module_by_code('shot')['id'], module_by_code('design')['id'], module_by_code('asset')['id'], module_by_code('level')['id']];

        if (count($entityFilter) > 0) {
            $entityFilter['_logic'] = 'OR';
            $entityFilter = ['entity.project_id' => $projectId, 'entity.module_id' => ['IN', $allowModuleIdList], [$entityFilter]];
        } else {
            $entityFilter['entity.project_id'] = $projectId;
            $entityFilter['entity.module_id'] = ['IN', $allowModuleIdList];
        }

        $stepNameList = StepService::getInstance()->getList(['name' => ['IN', implode(',', $stepNameList)]], 'id,name,code');
        $stepIdList = array_column($stepNameList, 'id');
        $entityIdList = model(EntityModel::class)->where($entityFilter)->field("id")->column('id');
        $taskList = [];
        if (!empty($entityIdList) && !empty($stepIdList)) {
            $taskList = model(StepModel::class)
                ->join("task on task.step_id = step.id")
                ->join("entity on entity.id = task.entity_id")
                ->where(['entity.id' => ['IN', $entityIdList], 'step.id' => ['IN', $stepIdList]])
                ->field("task.id,task.plan_delivery_time,task.episode_id,task.step_category_id,{$uk}")
                ->select();
        }
        $episodeIds = array_column($taskList, 'episode_id', 'episode_id');
        $stepCategoryIds = array_column($taskList, 'step_category_id', 'step_category_id');
        //排期时间查询
        $planTimeMap = PlanService::getInstance()->getStepCategoryPlanEndTime($episodeIds, $stepCategoryIds);
        $taskList = array_column($taskList, null, 'uk');
        unset($data);
        $allowUpdateFields = [
            'plan_start_time',
            'plan_end_time',
            'manual_man_hour',
        ];
        $dateFields = [
            'plan_start_time',
            'plan_end_time',
        ];
        $taskCommonService = CommonService::instance('task');
        // 查询项目信息
        $projectData = model(ProjectModel::class)->where(['id' => $projectId])->field('id,name,assign_task_is_limit_delivery_time')->find();
        foreach ($dataList as $data) {
            $data['flag'] = false;
            $data['err'] = null;
            $data['err_code'] = 0;
            if (!isset($taskList[$data['uk']])) {
                $data['err_code'] = ErrorCode::TASK_NOT_FOUND;
                $data['err'] = '任务不存在';
                $res[] = $data;
                continue;
            }
            $task = $taskList[$data['uk']];
            if ($task['plan_delivery_time'] > 0) {
                $planDeliveryTime = $task['plan_delivery_time'];
            } else {
                $planDeliveryTime = $planTimeMap[$task['episode_id']][$task['step_category_id']] ?? 0;
            }
            // 限制分配时间
            if ($projectData['assign_task_is_limit_delivery_time'] == 'yes') {
                if (isset($data['plan_end_time_unix']) && $planDeliveryTime > 0 && $planDeliveryTime < $data['plan_end_time_unix']) {
                    $data['err_code'] = ErrorCode::OVERDUE_DELIVERY_TIME;
                    $data['err'] = '超出交付时间';
                    $res[] = $data;
                    continue;
                }
            }

            $data['flag'] = true;
            $data['id'] = $task['id'];

            $updateData = [];
            foreach ($data as $k => $v) {
                if (!in_array($k, $allowUpdateFields)) {
                    continue;
                }
                if (in_array($k, $dateFields) && strtotime($v) === false) {
                    continue;
                }
                $updateData[$k] = $v;
            }

            if (empty($updateData)) {
                $data['err_code'] = ErrorCode::NO_DATA_HAS_BEEN_CHANGED;
                $data['err'] = '不需要更新';
                $res[] = $data;
                continue;
            }
            $updateData[$k] = $v;
            $updateData['id'] = $task['id'];

            $data['update_result'] = $taskCommonService->update($updateData, 'task', false);
            $res[] = $data;
        }
        return $res;
    }
}
